#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/kernel_stat.h>
#include <linux/version.h>
#include <linux/ktime.h>

#include <linux/mm.h>

#define PERF_CPU_NUM 8

static struct timer_list my_timer;
static ktime_t stat_start_time;

static void calculate_cpu_usage(struct timer_list *t)
{
    static int64_t prev_total_jiffies[PERF_CPU_NUM] = {0};
    static int64_t prev_idle_jiffies[PERF_CPU_NUM] = {0};
    int cpu;
    int32_t cpu_usage[PERF_CPU_NUM] = {0};

    for_each_possible_cpu(cpu) 
    {
        const struct kernel_cpustat *ksp = &kcpustat_cpu(cpu);
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
        int64_t user = kcpustat_field(ksp, CPUTIME_USER, cpu);
        int64_t nice = kcpustat_field(ksp, CPUTIME_NICE, cpu);
        int64_t system = kcpustat_field(ksp, CPUTIME_SYSTEM, cpu);
        int64_t idle = kcpustat_field(ksp, CPUTIME_IDLE, cpu);
        int64_t iowait = kcpustat_field(ksp, CPUTIME_IOWAIT, cpu);
        int64_t irq = kcpustat_field(ksp, CPUTIME_IRQ, cpu);
        int64_t softirq = kcpustat_field(ksp, CPUTIME_SOFTIRQ, cpu);
        int64_t steal = kcpustat_field(ksp, CPUTIME_STEAL, cpu);
        int64_t guest = kcpustat_field(ksp, CPUTIME_GUEST, cpu);
        int64_t guest_nice = kcpustat_field(ksp, CPUTIME_GUEST_NICE, cpu);
    #else
        int64_t user = ksp->cpustat[CPUTIME_USER];
        int64_t nice = ksp->cpustat[CPUTIME_NICE];
        int64_t system = ksp->cpustat[CPUTIME_SYSTEM];
        int64_t idle = ksp->cpustat[CPUTIME_IDLE];
        int64_t iowait = ksp->cpustat[CPUTIME_IOWAIT];
        int64_t irq = ksp->cpustat[CPUTIME_IRQ];
        int64_t softirq = ksp->cpustat[CPUTIME_SOFTIRQ];
        int64_t steal = ksp->cpustat[CPUTIME_STEAL];
        int64_t guest = ksp->cpustat[CPUTIME_GUEST];
        int64_t guest_nice = ksp->cpustat[CPUTIME_GUEST_NICE];
    #endif

        int64_t total_jiffies = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;
        int64_t idle_jiffies = idle + iowait;
        int64_t total_diff = total_jiffies - prev_total_jiffies[cpu];
        int64_t idle_diff = idle_jiffies - prev_idle_jiffies[cpu];

        if (total_diff > 0) {
            cpu_usage[cpu] = (int32_t)((total_diff - idle_diff) * 100 / total_diff);
        } else {
            cpu_usage[cpu] = 0;
        }

        prev_total_jiffies[cpu] = total_jiffies;
        prev_idle_jiffies[cpu] = idle_jiffies;

        if (cpu == PERF_CPU_NUM - 1)
        {
            break;
        }
    }

    {
        char str_usage[256] = {0};
        char* pstr = str_usage;
        ktime_t current_time = ktime_get();
        int32_t elapsed_ms = (int32_t)ktime_to_ms(ktime_sub(current_time, stat_start_time));

        for (cpu = 0; cpu < PERF_CPU_NUM; cpu++)
        {
            int write_len = sprintf(pstr, "%d%% ", cpu_usage[cpu]);
            pstr += write_len;
        }
        
        printk(KERN_INFO "%d cpu_usage: %s", elapsed_ms, str_usage);
    }

    {
        long total_ram, free_ram, cached_buffer_ram, sreclaimable_ram;
        long used_ram;

        struct sysinfo si;
        /**
         * 比较精确的已使用物理内存是free命令中的"used"值
         * 但sysinfo结构体中没有usedram成员，只能用其他方式计算
         * 计算方法是：total - free - (buffers + cached) - sreclaimable
         * 具体各值计算见fs/proc/meminfo.c meminfo_proc_show()函数
         */
        si_meminfo(&si);
        // 下面三个值的单位都是PAGES
        total_ram = si.totalram;
        free_ram = si.freeram;
        cached_buffer_ram = global_node_page_state(NR_FILE_PAGES);
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
        sreclaimable_ram = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B);
        #else
        sreclaimable_ram = global_node_page_state(NR_SLAB_RECLAIMABLE);
        #endif
        used_ram = (uint32_t)((total_ram - free_ram - cached_buffer_ram - sreclaimable_ram) * si.mem_unit / 1024);
        printk(KERN_INFO "total used ram kb: %ld\n", used_ram);
    }

    mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
}

static int __init cpu_usage_init(void)
{
    // 初始化定时器
    stat_start_time = ktime_get();
    timer_setup(&my_timer, calculate_cpu_usage, 0);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));  // 第一次延迟1秒后启动

    printk(KERN_INFO "CPU usage module loaded\n");
    return 0;
}

static void __exit cpu_usage_exit(void)
{
    del_timer_sync(&my_timer);
    printk(KERN_INFO "CPU usage module unloaded\n");
}

module_init(cpu_usage_init);
module_exit(cpu_usage_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple module to show CPU usage using kernel API");